// 1.AST改造require并将引入路径改为相对路径（后续要统一作为依赖树的key）readInfo可以读取 该module的 key、所有依赖的key、code
// 2.递归构建依赖树 graphArr 存所有Info
// 3.打包：依赖树map为module和exports module_cache存exports 最后_require_入口文件



const fs = require('fs')
const path = require('path')
// 解析代码产生AST
const parser = require('@babel/parser')
// 用于遍历AST 找到require并修改为_require_ 将引入路径改为相对路径
const traverse = require('@babel/traverse').default
// 将修改后的AST转换成新的代码输出
const babel = require('@babel/core')

const root = process.cwd()

function readModuleInfo(filePath) {
    // 准备相对路径作为module的key
    filePath = 
        './' + path.relative(root, path.resolve(filePath)).replace(/\\+/g, '/')
    //读取源码
    const content = fs.readFileSync(filePath, 'utf-8')
    //转换出AST
    const ast = parser.parse(content)
    //遍历模块的AST，将依赖收集到deps数组中
    const deps = []
    traverse(ast, {
        CallExpression: ({node}) => {
            // console.log(node)
            //如果是require 则收集依赖
            if(node.callee.name === 'require') {
                // 改造 require 关键字
                node.callee.name = '_require_'
                let moduleName = node.arguments[0].value
                // 增加扩展名
                moduleName += path.extname(moduleName) ? '' : '.js'
                // 拼接相对路径 和 文件名
                moduleName = path.join(path.dirname(filePath), moduleName)
                // 从 工作路径到moduleName的相对路径
                console.log(root, moduleName, path.relative(root, moduleName))
                moduleName = './' + path.relative(root, moduleName).replace(/\\+/g, '/')
                deps.push(moduleName)
                
                // 改造依赖的路径
                node.arguments[0].value = moduleName
            }
        }
    })

    // 编译回代码
    const {code} = babel.transformFromAst(ast)
    // 改造完成的代码 console.log(code)
    // const {
    //     name
    //   } = _require_("./src/b.js");
      
    //   console.log(name);

    return {
        // 入口文件的相对路径
        filePath,
        // 依赖的 相对路径数组
        deps,
        // 改造完成代码
        code,
    }
}

// 从入口递归找到所有被依赖模块 构成依赖树
function buildDependencyGraph(entry) {
    // 获取入口模块信息
    const entryInfo = readModuleInfo(entry)
    // 项目依赖树
    const graphArr = []
    // 加入入口文件信息ModuleInfo
    graphArr.push(entryInfo)
    // 从入口模块触发，递归地找每个模块的依赖，并将每个模块保存到graphArr
    for(const mod of graphArr) {
        mod.deps.forEach((depPath) => {
            const modInfo = readModuleInfo(path.resolve(depPath))
            graphArr.push(modInfo)
        })
    }
    return graphArr
}


// 上面的步骤全部是用来构建依赖树的 
// 依赖树扩展出 modules
// _require_(module) 返回 module.export 并存于 module_cache中

function pack(graph, entry) {
    const moduleArr = graph.map((module) => {
        return (
            `"${module.filePath}": function(module, exports, _require_) {
                eval(\`` + 
                    module.code + `\`
                )
            }`
        )
    })
    const output = `;(() => {
        var modules = {
            ${moduleArr.join(',\n')}
        }
        var modules_cache = {}
        var _require_ = function(moduleId) {
            if(modules_cache[moduleId])
                return modules_cache[moduleId].exports

            var module = modules_cache[moduleId] = {
                exports: {}
            }
            modules[moduleId](module, module.exports, _require_)
            return module.exports
        }

        _require_('${entry}')
    })()`
    return output
} 

function main(entry = './src/index.js', output = './dist.js') {
    fs.writeFileSync(output, pack(buildDependencyGraph(entry), entry))
}
main()
// fs.writeFileSync( file, data, options ) 文件路径名称 内容 参数